#! /usr/bin/env bash

if [ "$#" -lt 1 ]; then
	echo "Usage: $0 <user/repo> <tag> [only]" >&2
	exit 1
fi

repository="$1"
tag="$2"
shift 2
flags=("$@")

set -eo pipefail

function _curl () {
	local curl_args

	curl_args=(-s)
	if [ -n "${API_KEY}" ]; then
		curl_args+=(-H "Authorization: token ${API_KEY}")
	fi

	curl "${curl_args[@]}" "$@"
}

function milestones () {
	local repository

	repository="$1"

	(
		for state in closed open; do
			_curl "https://api.github.com/repos/${repository}/milestones?state=${state}&per_page=100" | jq -r '.[] | [.title, .number] | @tsv' || return 1
		done
	) | sort -rV
}

function tag_to_tag_date () {
	local tag
	local tag_url

	tag="$1"

	tag_url="$(_curl "https://api.github.com/repos/${repository}/git/refs/tags/${tag}" | jq -r 'if (type == "object") then . else .[] end | select(.ref=="refs/tags/'"${tag}"'") | .object.url')" || return 1
	_curl "${tag_url}" | jq -r '.tagger.date | fromdateiso8601 | strftime("%Y-%m-%d")' 2>/dev/null || \
		_curl "${tag_url}" | jq -r '.committer.date | fromdateiso8601 | strftime("%Y-%m-%d")' || return 1
}

function parts_of_milestone () {
	local repository milestone_id
	local state

	repository="$1"
	milestone_id="$2"

	for state in closed open; do
		page='0'
		while true; do
			page=$[${page} + 1]
			data="$(_curl "https://api.github.com/repos/${repository}/issues?milestone=${milestone_id}&state=${state}&page=${page}")"

			item_count="$(echo "${data}" | jq -r '. | length')"
			if [ "${item_count}" = '0' ]; then
				break
			fi

			echo "${data}" | \
				jq -r '.[] | [.html_url, .number, .title, (.labels[] | .name)] | @tsv' || return 1
		done
	done
}

function sanitize_markdown () {
	sed -r 's@(\[|\]|_|#)@\\&@g'
}

function print_item () {
	local item_id
	local item_name item_url

	item_id="$1"

	item_name="${itemToName[${item_id}]}"
	item_url="${itemToURL[${item_id}]}"

	echo " - ${item_name} \[[\#${item_id}](${item_url})\]"
}

function changelog () {
	local repository start_tag flags_list
	local milestones_info previous_milestones previous_milestone
	local milestone_name previous_milestone_name milestone_date tag_name
	local itemToTags itemToName itemURL categoryIds categoryId
	local majorIds item_url item_id item_name item_tags skip_item
	local category is_major breaking
	local start_output flags flag_only

	echo "# Change Log"

	repository="$1"
	start_tag="$2"
	flags_list="$3"

	flag_only='false'
	for flag in ${flags_list}; do
		case "${flag}" in
			only)
				if [ -n "${start_tag}" ]; then
					flag_only='true'
				fi
				;;
		esac
	done

	# Get milestone information
	milestones_info="$(milestones "${repository}")" || return 1

	# Compute previous version information
	unset previous_milestones
	declare -A previous_milestones
	milestone_name=''
	while IFS=$'\t' read -r milestone_name milestone_id; do
		previous_milestones["${milestone_name}"]="${previous_milestone}"
		previous_milestone="${milestone_name}"
	done < <(echo "${milestones_info}" | sort -V)

	# Process all milestones
	if [ -n "${start_tag}" ]; then
		start_output='false'
	else
		start_output='true'
	fi
	start_tag_check="$(echo "${start_tag}" | sed 's@RC[0-9]*$@@')"
	while IFS=$'\t' read -r milestone_name milestone_id; do
		echo "Processing milestone: ${milestone_name} ..." >&2

		tag_name="${milestone_name}"
		if [ "${start_output}" = 'false' ]; then
			if [ "${milestone_name}" = "${start_tag_check}" ]; then
				start_output='true'

				tag_name="${start_tag}"
			fi
		fi

		if [ "${start_output}" = 'false' ]; then
			continue
		fi

		milestone_date="$(tag_to_tag_date "${tag_name}")" || milestone_date=''
		previous_milestone_name="${previous_milestones[${milestone_name}]}"
		if [ -z "${milestone_date}" -o -z "${previous_milestone_name}" ]; then
			continue
		fi

		echo "## Release [${tag_name}](https://github.com/nanocurrency/nano-node/tree/${tag_name}) (${milestone_date})"
		echo ""

		echo "[Full Changelog](https://github.com/nanocurrency/nano-node/compare/${previous_milestone_name}...${tag_name})"
		echo ""

		unset itemToTags itemToName itemURL categoryIds
		declare -A itemToTags itemToName itemToURL
		declare -A categoryIds
		majorIds=()
		while IFS=$'\t' read -r item_url item_id item_name item_tags; do
			skip_item='false'
			is_major='false'
			category=''
			for item_tag in ${item_tags}; do
				case "${item_tag}" in
					wontfix|duplicate|invalid)
						skip_item='true'
						;;
					enhancement|quality)
						category='Implemented enhancements'
						;;
					major|semantics)
						is_major='true'
						;;
					bug)
						category='Fixed bugs'
						;;
				esac
			done
			if [ -z "${category}" ]; then
				category='Fixed bugs'
			fi

			if [ "${skip_item}" = 'true' ]; then
				continue
			fi

			breaking='false'
			item_name="$(echo "${item_name}" | sanitize_markdown)"

			itemToTags[$item_id]="${item_tags}"
			itemToName[$item_id]="${item_name}"
			itemToURL[$item_id]="${item_url}"

			categoryIds[${category}]+=" $item_id"
			if [ "${is_major}" = 'true' ]; then
				majorIds+=($item_id)
			fi
		done < <(parts_of_milestone "${repository}" "${milestone_id}")

		if [ "${#majorIds[@]}" -gt 0 ]; then
			echo "**Major Changes:**"
			for item_id in "${majorIds[@]}"; do
				print_item "${item_id}"
			done
			echo ""
		fi

		for categoryId in 'Implemented enhancements' 'Fixed bugs'; do
			if [ -n "${categoryIds[${categoryId}]}" ]; then
				echo "**${categoryId}:**"
				for item_id in ${categoryIds[${categoryId}]}; do
					print_item "${item_id}"
				done
				echo ""
			fi
		done

		if [ "${flag_only}" = 'true' ]; then
			break
		fi

		echo ""
		echo ""
	done <<<"${milestones_info}"
}

changelog="$(changelog "${repository}" "${tag}" "${flags[*]}")"

echo "${changelog}" > CHANGELOG.md
